/* Soubor je ulozen v kodovani UTF-8.
 * Kontrola kódování: Příliš žluťoučký kůň úpěl ďábelské ódy.
 */
package cz.pecinovsky.česky.oopnz.tvary;

import cz.pecinovsky.česky.oopnz.utility.IO;



////////////////////////////////////////////////////////////////////////////////
//%%+  +++++ Konec ignorovaného počátečního textu - prostor pro importy +++++

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//%X+ xxxxx Začátek přeskakovaného textu s deklarací obalové třídy xxxxx
/*******************************************************************************
 *<pre>
 * Předchozí:   Není - toto_je_nově_definovaná_třída
 * Následující: Prozatím není
 *
 * Projekt  110z - Rozhraní
 *   + Zavedena tato třída
 *     Třída impelementuje rozhraní {@link ITvar},
 *     avšak anotace {@code @Override} zůstávají mimo hru,
 *     aby si studenti mohli vyzkoušet zrušení implementace rozhraní
 *     a třída šla přesto nadále přeložit
 *
 * Projekt  112z - Úvod do návrhových vzorů
 *   ~ Implementuje rozhraní {@code ITvar, IPosuvný, IHýbací, INafukovací},
 *     ale jenom pro tento jeden projekt, pak se vrací k předchozímu
 *
 * Projekt  Ppp
 *   + Přidáno
 *   - Odebráno
 *   ~ Změněno
 *</pre>
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
////////////////////////////////////////////////////////////////////////////////
//%X- ----- Konec přeskakovaného textu s deklarací obalové třídy -----


/*******************************************************************************
 * Instance třídy {@code Mnohotvar} předstvují složitější geometrické tvary
 * určené pro práci na virtuálním plátně
 * při prvním seznámení s třídami a objekty.
 * Tyto tvary mohou být složeny z několika tvarů jednodušších,
 * které jsou instancemi rozhraní {@link ITvar}.
 * <p>
 * Mnohotvar je postupně skládán z řady jednodušších tvarů,
 * které musejí být instancemi rozhraní {@link ITvar}.
 * Jiné požadavky na ně kladeny nejsou.
 * Při sestavování mnohotvar automaticky upravuje
 * interní informaci o své pozici a rozměru tak,
 * aby pozice byla neustále v levém rohu opsaného obdélníku
 * a rozměr mnohotvaru odpovídal rozměru tohoto obdélníku.
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public class Mnohotvar
//%X+ =112z
       implements ITvar
//%X-
//%I+ =112z
//       implements ITvar, IPosuvný, IHýbací, INafukovací
//%I-
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Maximální povolená velikost kroku. */
    public static final int MAX_KROK = 100;

    /** Mapa všech vytvořených a dosud nezrušených mnohotvarů
     *  indexovaná jejich názvy. */
    private static final Map<String,Mnohotvar> název2mnohotvar =
                                               new HashMap<String, Mnohotvar>();



//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

    /** Počet pixelů, o něž se instance posune
     *  po bezparametrickém posunovém povelu. */
    private static int krok = 50;

    /** Počet vytvořených instancí. */
    private static int počet = 0;



//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** ID instance = pořadí vytvoření dané instance v rámci třídy. */
    private final int ID = ++počet;

    /** Název zadaný při konstrukci objektu.
     *  Tento název umožnuje blíže specifikovat příslušný mnohotvar. */
    private final String název;

    /** Seznam prvků, z nichž se mnohotvar skládá. */
    private final List<Část> seznam = new ArrayList<Část>();



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Dokud je atribut false, je možné do mnohotvaru
     *  přidávat další součásti. */
    private boolean tvorbaUkončena = false;

    /** Bodová x-ová souřadnice instance. */
    private int xPos;

    /** Bodová y-ová souřadnice instance. */
    private int yPos;

    /** Šířka v bodech. */
    protected int šířka;

    /** Výška v bodech. */
    protected int výška;



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================

    /***************************************************************************
     * Vrátí implicitní vzdálenost (krok), o kterou se instance přesune
     * při volaní bezparametrickych metod přesunu.
     *
     * @return Velikost implicitního kroku v bodech
     */
     public static int getKrok()
     {
         return krok;
     }


    /***************************************************************************
     * Nastaví implicitní vzdálenost (krok), o kterou se instance přesune
     * při volaní bezparametrickych metod přesunu.
     *
     * @param velikost  Velikost implicitního kroku v bodech;<br/>
     *                  musí platit:  0 &lt;= velikost &lt;= {@link #MAX_KROK}
     */
    public static void setKrok( int velikost )
    {
        if( (velikost < 0)  || (velikost > MAX_KROK) ) {
            throw new IllegalArgumentException(
                "\nKrok musí byt z intervalu <0;" + MAX_KROK + ">." );
        }
        krok = velikost;
    }



//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Vrátí pole názvů všech evidovaných mnohotvarů.
     *
     * @return Pole názvů evidovaných mnohotvarů
     */
    public static String[] názvyDostupných()
    {
        int početDostupných = název2mnohotvar.size();
        String[] ret = new String[početDostupných];
        int i = 0;
        for (Map.Entry<String,Mnohotvar> me : název2mnohotvar.entrySet()) {
            ret[i] = me.getKey();
        }
        return ret;
    }



//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vrátí instanci se zadaným názvem.
     *
     * @param název Název požadované instance
     * @return Požadovaná instance
     * @throws IllegalArgumentException
     *         Není-li evidována instance se zadaným názvem
     */
    public Mnohotvar getInstance(String název)
    {
        Mnohotvar ret = název2mnohotvar.get(název);
        if (ret == null) {
            throw new IllegalArgumentException(
                "\nMnohotvar se zadaným názvem není evidován: " + název);
        }
        return ret;
    }


    /***************************************************************************
     * Vytvoří mnohotvar se zadaným názvem skládající se ze zadaných objektů;
     * do tohoto mnohotvaru již nebude možno přidávat další objekty.
     *
     * @param název Název vytvářeného mnohotvaru.
     *              Je-li zadán prázdný řetězec nebo {@code null},
     *              konstruktor mu přiřadí název ve tvaru
     *              {@code Mnohotvar#}<i>i</i>, kde <i>i</i> je celé číslo.
     * @param části Jednotlivé tvary, z nichž je mnohotvar složen
     */
    public Mnohotvar(String název, ITvar... části)
    {
        this(false, název, části);
    }


    /***************************************************************************
     * Vytvoří prázdný mnohotvar se zadaným názvem očekávající,
     * že jeho jednotlivé části budou teprve dodány pomocí metody
     * {@link #přidej(ITvar...)}.
     * <p>
     * Ukončení sestavování mnohotvaru je třeba oznámit zavoláním metody
     * {@link #hotovo()}.
     * Dokud není sestavování ukončeno,
     * není možno nastavovat pozici ani rozměr vznikajícího mnohotvaru.
     * Je však možno se na ně zeptat
     * a současně je možno rozdělaný mnohotvar nakreslit.
     *
     * @param název Název vytvářeného mnohotvaru.
     *              Je-li zadán prázdný řetězec nebo {@code null},
     *              konstruktor mu přiřadí název ve tvaru
     *              {@code Mnohotvar#}<i>i</i>, kde <i>i</i> je celé číslo.
     */
    public Mnohotvar(String název)
    {
        this(false, název, (ITvar[])null);
    }


    /***************************************************************************
     * Vytvoří mnohotvar se zadaným názvem
     * a předběžně naplněný zadanými objekty -- jeho částmi.
     * Vytvořený mnohotvar však není uzavřey vůči přidání dalších částí
     * pomocí metody {@link #přidej(ITvar...)}.
     *
     * @param interní Příznak tvorby interní kopie existujícího tvaru,
     *                která nebude mít název
     *                a nebude zařazena mezi evidované
     * @param název  Název vytvářeného mnohotvaru.
     *              Je-li zadán prázdný řetězec nebo {@code null},
     *              konstruktor mu přiřadí název ve tvaru
     *              {@code Mnohotvar#}<i>i</i>, kde <i>i</i> je celé číslo.
     * @param části  Jednotlivé tvary, z nichž bude mnohotvar složen
     */
    private Mnohotvar(boolean interní, String název, ITvar... části)
    {
        if (! interní) {
            //Není-li název zadán, vymyslí vlastní
            if ((název == null)  ||  (název.equals(""))) {
                název = názevProKopii("Mnohotvar");
            }
            else { //Je zadán => zkontroluje jeho jedinečnost
                ověřNázev(název);
            }
        }
        this.název = název;

        if (! interní) {
            Mnohotvar já = this;    //Aby nevaroval
            název2mnohotvar.put(název, já);
        }

        //Byly-li zadány části, sestaví a uzavře vytvářený tvar
        if ((části != null)  &&  (části.length > 0)) {
            přidej(části);
            tvorbaUkončena = true;
        }
    }


    /***************************************************************************
     * Vytvoří stejně velkou a stejně umístěnou kopii daného mnohotvaru
     * a přidělí jí vlastní název ve tvaru {@code PůvodníNázev#?},
     * kde otazník zastupuje nejmenší celé kladné číslo,
     * pro něž není evidován mnohotvar s takto vytvořeným názvem.
     *
     * @return Požadovaná kopie
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public ITvar kopie()
    {
        String názevKopie = (název == null)
                          ? null
                          : názevProKopii(this.název);
        ITvar  kopie = kopie(false, názevKopie);
        return kopie;
    }


    /***************************************************************************
     * Vytvoří kopii daného mnohotvaru; kopie bude mít zadaný název,
     * který se však nesmí shodovat s názvem žádného evidovaného mnohotvaru.
     *
     * @param  názevKopie Název požadované kopie
     * @return Požadovaná kopie
     */
    public ITvar kopie(String názevKopie)
    {
        return kopie(false, názevKopie);
    }


    /***************************************************************************
     * Vytvoří kopii daného mnohotvaru; kopie bude mít zadaný název,
     * který se však nesmí shodovat s názvem žádného evidovaného mnohotvaru.
     *
     * @param  interní    {@code true} značí kopii pro interní potřebu,
     *                    a ta nepotřebuje název
     * @param  názevKopie Název požadované kopie
     * @return Požadovaná kopie
     */
    private ITvar kopie(final boolean interní, final String názevKopie)
    {
        if (! interní) {
            ověřNázev(názevKopie);
        }
        int početSoučástí  = seznam.size();
        final ITvar[] ait = new ITvar[početSoučástí];
        for (int i = 0;   i < početSoučástí;   i++) {
            ait[i] = seznam.get(i).tvar;
        }
        if (this instanceof ITvar) {
            return (ITvar)(new Mnohotvar(interní, názevKopie, ait));
        } else {
            //Pomocná třída zabezpečující správnou funkci i v případě,
            //kdy třída nebude implementovat rozhraní ITvar
            class TM extends Mnohotvar implements ITvar {
                TM(String názevKopie, ITvar[] ait) {
                    super(interní, názevKopie, ait);
                }
            }
            return new TM(názevKopie, ait);
        }
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí x-ovou (vodorovnou) souřadnici pozice instance,
     * tj. vodorovnou souřadnici levého horního rohu opsaného obdélníku.
     *
     * @return  Aktuální vodorovná (x-ová) souřadnice instance,
     *          x=0 má levý okraj plátna, souřadnice roste doprava
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public int getX()
    {
        return xPos;
    }


    /***************************************************************************
     * Vrátí y-ovou (svislou) souřadnici pozice instance,
     * tj. svislou souřadnici levého horního rohu opsaného obdélníku.
     *
     * @return  Aktuální svislá (y-ová) souřadnice instance,
     *          y=0 má horní okraj plátna, souřadnice roste dolů
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public int getY()
    {
        return yPos;
    }


    /***************************************************************************
     * Přemístí celý mnohotvar na zadanou pozici.
     * Všechny součásti instance se přemisťují jako celek.
     * Pozice instance jsou přitom definovány jako pozice
     * levého horního rohu opsaného obdélníku.
     *
     * @param x  Nově nastavovaná vodorovná (x-ová) souřadnice instance,
     *           x=0 má levý okraj plátna, souřadnice roste doprava
     * @param y  Nově nastavovaná svislá (y-ová) souřadnice instance,
     *           y=0 má horní okraj plátna, souřadnice roste dolů
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public void setPozice(int x, int y)
    {
        ověřDokončenost();
        int dx = x - getX();
        int dy = y - getY();
        for( Část č : seznam )
        {
            ITvar tvar = č.tvar;
            tvar.setPozice( dx + tvar.getX(), dy + tvar.getY() );
        }
        this.xPos = x;    //Nastavuji hodnoty pro celý tvar
        this.yPos = y;
        nakresli();
    }


    /***************************************************************************
     * Vrátí šířku instance v bodech.
     * Šířka instance jsou přitom definována jako šířka
     * opsaného obdélníku.
     *
     * @return  Aktuální šířka instance v bodech
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
     public int getŠířka()
     {
         return šířka;
     }


    /***************************************************************************
     * Vrátí výšku instance v bodech.
     * Výška instance jsou přitom definována jako výška
     * opsaného obdélníku.
     *
     * @return  Aktuální výška instance v bodech
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
     public int getVýška()
     {
         return výška;
     }


    /***************************************************************************
     * Nastaví nový "čtvercový" rozměr instance -
     * na zadaný rozměr se nastaví výška i šířka.
     * Rozměry instance jsou přitom definovány jako rozměry
     * opsaného obdélníku.
     * Nastavované rozměry musí být nezáporné,
     * místo nulového rozměru se nastaví rozměr rovný jedné.
     *
     * @param rozměr  Nově nastavovaný rozměr v obou směrech; rozměr>0
     */
    public void setRozměr(int rozměr)
    {
        setRozměr( rozměr, rozměr );
    }


    /***************************************************************************
     * Nastaví nový rozměr mnohotvaru.
     * Upraví rozměry a pozice všech jeho součástí tak,
     * aby výsledný mnohotvar měl i při novém rozměru
     * stále stejný celkový vzhled.
     * Rozměry instance jsou přitom definovány jako rozměry
     * opsaného obdélníku.
     * Nastavované rozměry musí být nezáporné,
     * místo nulového rozměru se nastaví rozměr rovný jedné.
     *
     * @param šířka    Nově nastavovaná šířka; šířka >= 0
     * @param výška    Nově nastavovaná výška; výška >= 0
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public void setRozměr(int šířka, int výška)
    {
        ověřDokončenost();
        if ((šířka < 0) || (výška < 0)) {
            throw new IllegalArgumentException(
                                    "Rozměry musí byt nezáporné: šířka=" +
                                    šířka + ", výška=" + výška);
        }
        //Uprav velikosti a pozice jednotlivých částí
        for (Část č : seznam) {
            č.poNafouknutí(šířka, výška);
        }
        //Nastavuji hodnoty pro celý tvar
        this.šířka = Math.max(1, šířka);
        this.výška = Math.max(1, výška);
        nakresli();
    }


    /***************************************************************************
     * Vrátí název instance zadaný při konstrukci objektu.
     *
     * @return  Řetězec s názvem instance
     */
    public String getNázev()
    {
        return název;
    }



//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Vrátí podpis instance, tj. její řetězcovou reprezentaci.
     * Používá se především při ladění.
     *
     * @return Název instance následovaný jejími souřadnicemi,
     *         a rozměry
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public String toString()
    {
        return název + "[x=" + xPos + ", y=" + yPos  +
               ", šířka=" + šířka + ", výška=" + výška +
               ", ID=" + ID + "]";
    }


    /***************************************************************************
     * Vykreslí obraz své instance na plátno.
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public void nakresli()
    {
        for (Část část : seznam)
        {
            část.tvar.nakresli();
        }
    }


    /***************************************************************************
     * Smaže obraz své instance z plátna (nakreslí ji barvou pozadí plátna).
     */
//%A+ <0    "Abych odstranil varování editoru
    @Override
//%A-
    public void smaž()
    {
        for (Část část : seznam)
        {
            část.tvar.smaž();
        }
    }


    /***************************************************************************
     * Přesune instanci o zadaný počet bodů vpravo,
     * při záporné hodnotě parametru vlevo.
     *
     * @param vzdálenost Vzdálenost, o kterou se instance přesune
     */
    public void posunVpravo(int vzdálenost)
    {
        setPozice( xPos+vzdálenost, yPos );
    }


    /***************************************************************************
     * Přesune instanci o implicitní počet bodů vpravo.
     * Tento počet lze zjistit zavoláním statické metody {@link #getKrok()}
     * a nastavit zavoláním statické metody {@link #setKrok(int)}.
     */
    public void posunVpravo()
    {
        posunVpravo( krok );
    }


    /***************************************************************************
     * Přesune instanci o implicitní počet bodů vlevo.
     * Tento počet lze zjistit zavoláním statické metody {@link #getKrok()}
     * a nastavit zavoláním statické metody {@link #setKrok(int)}.
     */
    public void posunVlevo()
    {
        posunVpravo( -krok );
    }


    /***************************************************************************
     * Přesune instanci o zadaný počet bodů dolů,
     * při záporné hodnotě parametru nahoru.
     *
     * @param vzdálenost   Počet bodů, o které se instance přesune
     */
    public void posunDolů(int vzdálenost)
    {
        setPozice( xPos, yPos+vzdálenost );
    }


    /***************************************************************************
     * Přesune instanci o implicitní počet bodů dolů.
     * Tento počet lze zjistit zavoláním statické metody {@link #getKrok()}
     * a nastavit zavoláním statické metody {@link #setKrok(int)}.
     */
    public void posunDolů()
    {
        posunDolů( krok );
    }


    /***************************************************************************
     * Přesune instanci o implicitní počet bodů nahoru.
     * Tento počet lze zjistit zavoláním statické metody {@link #getKrok()}
     * a nastavit zavoláním statické metody {@link #setKrok(int)}.
     */
    public void posunVzhůru()
    {
        posunDolů( -krok );
    }


    /***************************************************************************
     * Ukončí tvrobu mnohotvaru.
     * Od této chvíle již nebude možno přidat žádný další objekt.
     */
    public void hotovo()
    {
        tvorbaUkončena = true;
    }


    /***************************************************************************
     * Přidá do mnohotvaru zadané prvky a příslušně upraví novou
     * pozici a velikost mnohotvaru.
     *
     * @param tvar  Přidávaný hýbací tvar
     */
    public final void přidej(ITvar... tvar)
    {
        if (tvorbaUkončena) {
            throw new IllegalStateException("\nPokus o přidání dalšího prvku " +
                    "po ukončení tvorby mnohotvaru " + název);
        }
        for (ITvar t : tvar) {
            ITvar it =  (t instanceof Mnohotvar)
                     ?  ((Mnohotvar)t).kopie(true, null)   //Interní kopie
                     :  t.kopie();
            přidejTvar(it);
        }
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Vrátí doposud nepoužitý název pro vytvářenou kopii dané instance.
     * <p>
     * Nemá-li originál jako svůj název {@code null},
     * bude vytvořený název ve tvaru {@code PůvodníNázev#?},
     * kde otazník zastupuje nejmenší celé kladné číslo,
     * pro něž není evidován mnohotvar s takto vytvořeným názvem.
     * <p>
     * Má-li aktuální mnohotvar název {@code null},
     * jedná se o interní mnohotvar, jenž je částí složitějšího celku.
     * Předpokládá se proto,
     * že jeho kopie se opět vytváří pouze pro zabudování do vyššího celku,
     * a tak může mít opět název {@code null}.
     *
     * @param název Název mnohotvaru jehož kopii cheme pojmenovat
     * @return Vytvořený unikátní název
     */
    private String názevProKopii(String název)
    {
        if (název == null) {
            return null;
        }
        String podkladek = název + "#";
        String novýNázev;
        int i = 1;
        do {
            novýNázev = podkladek + i++;
        } while (název2mnohotvar.containsKey(novýNázev));
        return novýNázev;
    }


    /***************************************************************************
     * Zkontroluje, je-li název v kolekci dosud vytvořených názvů,
     * a pokud je, tak vyhodí výjimku.
     *
     * @param název Testovaný název
     * @throws IllegalArgumentException Název je již v kolekci
     */
    private void ověřNázev(String název)
            throws IllegalArgumentException
    {
        if (název2mnohotvar.containsKey(název)) {
            throw new IllegalArgumentException(
                "\nMnohotvar nemá zadán jedinečný název: " + název);
        }
    }


    /***************************************************************************
     * Zkontroluje dokončenost konstrucke objektu a není-li objekt dokončen,
     * vyhodí výjimku {@code IllegalStateException}.
     *
     * @throws IllegalStateException Není-li dokončena konstrukce objektu
     */
    private void ověřDokončenost()
    {
        if (tvorbaUkončena) {
            return;
        }
        Throwable ex = new Throwable();
        StackTraceElement[] aste = ex.getStackTrace();
        String metoda = aste[1].getMethodName();
        throw new IllegalStateException(
            "\nNedokončený objekt nemůže spustit metodu: " + metoda);
    }


    /***************************************************************************
     * Přidá do mnohotvaru zadaný prvek a příslušně upraví novou
     * pozici a velikost mnohotvaru.
     *
     * @param tvar  Přidávaný tvar
     */
    private void přidejTvar(ITvar tvar)
    {
        //tx, y, s, v = x, y, šířka výška přidávaného tvaru
        int tx = tvar.getX();
        int ty = tvar.getY();
        int ts = tvar.getŠířka();
        int tv = tvar.getVýška();

        if( seznam.isEmpty() )  //Přidávaný tvar je prvním tvarem v mnohotvaru
        {
            xPos  = tx;
            yPos  = ty;
            šířka = ts;
            výška = tv;
            seznam.add(new Část(tvar));
            return;                     //==========>
        }

        //Přídávaný tvar není prvním
        //Zapamatuji si původní parametry, aby je pak bylo možno porovnávat
        //s upravenými po zahrnutí nového tvaru
        int mx = xPos;
        int my = yPos;
        int ms = šířka;
        int mv = výška;
        boolean změna = false;

        if( tx < xPos )
        {   //Přidávaný tvar zasahuje víc vlevo
            šířka += getX() - tx;
            xPos   = tx;
            změna = true;
        }
        if( ty < yPos )
        {   //Přidávaný tvar zasahuje víc nahoru
            výška += yPos - ty;
            yPos   = ty;
            změna = true;
        }
        if( (xPos + šířka) < (tx + ts) )
        {   //Přidávaný tvar zasahuje víc vpravo
            šířka = tx + ts - xPos;
            změna = true;
        }
        if( (yPos + výška) < (ty + tv) )
        {   //Přidávaný tvar zasahuje víc dolů
            výška = ty + tv - yPos;
            změna = true;
        }
        //Nyní mají atributy xPos, yPos, šířka a výška hodnoty
        //odpovídající mnohotvaru zahrnujícímu přidaný tvar

        //Pokud se něco změnilo, musím přepočítat parametry všech součástí
        if (změna) {
            for( Část č : seznam ) {
                č.poPřidání( mx, my, ms, mv);
            }
        }
        seznam.add(new Část(tvar));
    }


    /***************************************************************************
     * Nastaví nový název svého tvaru. Nekontroluje jeho jedinečnost -
     * o tu se musí postarat volající metoda.
     *
     * @param název Nastavovaný nový název
     */
    private void setNovýNázev(String název)
    {
        Class<?> cls = Mnohotvar.class;
        try {
            java.lang.reflect.Field fldNázev = cls.getDeclaredField("název");
            fldNázev.setAccessible(true);
            fldNázev.set(this, název);
        } catch(Exception ex) {
            throw new RuntimeException("\nReflexe má problémy", ex);
        }
    }



//== INTERNÍ DATOVÉ TYPY =======================================================

    /***************************************************************************
     * Instance třídy slouží jako přepravky pro uchovávání pomocných informací
     * pro co nelepší změnu velikosti mnohotvaru.
     */
    private final class Část
    {
    //== KONSTANTNÍ ATRIBUTY TŘÍDY =============================================
    //== PROMĚNNÉ ATRIBUTY TŘÍDY ===============================================
    //== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ====================
    //== KONSTANTNÍ ATRIBUTY INSTANCÍ ==========================================
    //== PROMĚNNÉ ATRIBUTY INSTANCÍ ============================================

        /** Odkaz na část mnohotvaru. */
        ITvar tvar;

        /** Podíl odstupu od levého kraje mnohotvaru
         *  na jeho celkové šířce. */
        double dx;

        /** Podíl odstupu od horního kraje mnohotvaru
         *  na jeho celkové výšce. */
        double dy;

        /** Podíl šířky části k celkové šířce mnohotvaru. */
        double dš;

        /** Podíl výšky části k celkové výšce mnohotvaru. */
        double dv;



    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ====================================
    //== OSTATNÍ NESOUKROMÉ METODY TŘÍDY =======================================

    //##########################################################################
    //== KONSTRUKTORY A TOVÁRNÍ METODY =========================================

        /***********************************************************************
         * Vytvoří přepravku a zapamatuje si aktuální stav některých poměrů
         * vůči současné porobě mnohotvaru.
         *
         * @param část Obalovaný tvar
         */
        Část(ITvar část)
        {
            this.tvar = část;
            int tx = část.getX();
            int ty = část.getY();
            int ts = část.getŠířka();
            int tv = část.getVýška();
            dx = (tx - xPos) / (double)šířka;
            dy = (ty - yPos) / (double)výška;
            dš = ts          / (double)šířka;
            dv = tv          / (double)výška;
        }



    //== ABSTRAKTNÍ METODY =====================================================
    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =================================
    //== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ====================================

        /***********************************************************************
         * Aktualizuje uchovávanou relativní pozici a rozměry dané součásti
         * v rámci celého mnohotvaru  po přidání nové součásti
         * vedoucí ke změně pozice a/nebo rozměru mnohotvaru.
         *
         * @param sx  Původní (staré) x
         * @param sy  Původní (staré) y
         * @param sš  Původní (stará) šířka
         * @param sv  Původní (stará) výška
         */
        void poPřidání(int sx, int sy, int sš, int sv)
        {
            //Souřadnice se mohou pouze zmenšovat
            dx = (sx - xPos + dx*sš) / šířka;
            dy = (sy - yPos + dy*sv) / výška;

            dš = dš * sš / šířka;
            dv = dv * sv / výška;
        }


        /***********************************************************************
         * Aktiualizuje uchovávanou relativní pozici a rozměry dané součásti
         * v rámci celého mnohotvaru po změně jeho velikosti.
         *
         * @param šířka  Nastavovaná šířka celého mnohotvaru
         * @param výška  Nastavovaná výška celého mnohotvaru
         */
        void poNafouknutí(int šířka, int výška)
        {
            tvar.setPozice( (int)Math.round( Mnohotvar.this.xPos + dx*šířka ),
                            (int)Math.round( Mnohotvar.this.yPos + dy*výška ));
            tvar.setRozměr( (int)Math.round( šířka*dš ),
                            (int)Math.round( výška*dv ) );
        }


        /***********************************************************************
         * Vrátí textovou reprezentaci všech atributů.
         *
         * @return Textová reprezentace všech atributů
         */
    @Override
        public String toString()
        {
            return "Část[tvar=" + tvar + ", dx=" + dx + ", dy=" + dy +
                   ", dš=" + dš + ", dv=" + dv + "]";
        }



    //== SOUKROMÉ A POMOCNÉ METODY TŘÍDY =======================================
    //== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ====================================
    //== INTERNÍ DATOVÉ TYPY ===================================================
    //== TESTY A METODA MAIN ===================================================
    }



//== TESTOVACÍ METODY A TŘÍDY ==================================================

    /***************************************************************************
     * Interní třída určená k tomu, aby testovací metoda mohla spustit
     * posloupnost testů nad jedním přípravkem.
     * Její jedinou metodou je metoda {@link #odregistrujVše()},
     * která vyčistí interní mapu pamatující si všechny pojmenované mnohotvary.
     * <p>
     * Třída by správně neměla být veřejná.
     * Umístování testů do stejného balíčku v testovacím stromu balíčků
     * je však probíráno až v pokračovacím kurzu,
     * tak je vše definováno tak,
     * aby mohla být testovací metoda i v jiném balíčku.
     */
    public static class Test
    {
        /***********************************************************************
         * Vyčistí interní mapu pamatující si všechny pojmenované mnohotvary.
         */
        public static void odregistrujVše()
        {
            název2mnohotvar.clear();
        }


        private Test() {}
    }

//%X+

    /***************************************************************************
     * Testovací metoda.
     */
    public static void test()
    {
        Obdélník    o1 = new Obdélník();
        Elipsa      e1 = new Elipsa();
        Trojúhelník t1 = new Trojúhelník();

        Mnohotvar m1 = new Mnohotvar(null, o1, e1, t1);
        Mnohotvar m2 = new Mnohotvar("");

        m2.přidej(m1.kopie());
        m1.setRozměr(33, 25);   m1.setPozice(33, 25);
        m2.přidej(m1.kopie());
        m2.hotovo();
        m2.nakresli();
        IO.zpráva("Vytvořeny mnohohotvary:\n" + m1 + "\n" + m2);

        m1.setPozice(0, 0);     m1.setRozměr(300);
        IO.zpráva(m1 + " zvětšen na [300,300]");

        m1.setRozměr(7, 7);
        IO.zpráva(m1 + " zmenšen na [7,7]");

        m1.setPozice(150, 200);
        m1.setRozměr(150, 100);
        m2.nakresli();
        IO.zpráva(m1 + "\nsrovnán na [150,100] a přestěhován na [150, 200]," +
                     "\n\nZobrazen " + m2);

        m2.setPozice(50, 50);
        m2.setRozměr(200, 200);

        Mnohotvar m2k = (Mnohotvar)m2.kopie("Kopie_m2");
        Mnohotvar m11 = (Mnohotvar)m1 .kopie();
        Mnohotvar m12 = (Mnohotvar)m1 .kopie();
        Mnohotvar m111= (Mnohotvar)m11.kopie();

        IO.zpráva("Seznam vytvořených mnohotvarů:" +
                "\nm1:   " + m1  +
                "\nm2:   " + m2  +
                "\nm2k:  " + m2k +
                "\nm11:  " + m11 +
                "\nm12:  " + m12 +
                "\nm111: " + m111 +
                "");

        Test.odregistrujVše();
        System.exit(0);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main( String[] args )  {  test();  }
//%X-
}
